function ajax() {
    for (var i = 0, len = arguments.length, arg, attr; i < len; i++) {
        arg = arguments[i];
        this.is_function(arg) && arg.call(this);
        if (this.is_object(arg) || this.is_array(arg)) {
            for (attr in arg) {
                ({}).hasOwnProperty.call(arg, attr) && (this[attr] = arg[attr]);
            }
        }
    }
    this.method = "POST";
    this.async = true; //是否异步请求
    this.contentType = "application/x-www-form-urlencoded"; //内容类型
    this.contentTypeList = {
        default: "application/x-www-form-urlencoded",
        file: "multipart/form-data",
        json: "application/json",
        text: "text/plain"
    };
    this.ajax = null; // ajax 对象
    /**
     * 初始化
     * @param method
     * @param {boolean} async
     * @param contentType
     * @returns {ajax}
     */
    this.init = function (method, async, contentType) {
        this.ajax = null , this.ajax = this.xhr();
        this.y_string(method) && (this.method = method.toLocaleUpperCase());
        async !== undefined && (this.async = !!async);
        this.y_string(contentType) && (this.contentType = this.contentTypeList[contentType] ? this.contentTypeList[contentType] : contentType);
        return this;
    };
    /**
     * @param key
     * @param value
     * @returns {ajax}
     */
    this.set = function (key, value) {
        if (this.y_string(key) && this.y_string(value)) {
            this.is_function(this[key]) || (this[key] = value);
        } else if (this.is_object(key)) {
            for (var k in key) {
                ({}).hasOwnProperty.call(key, k) && this.set(k, key[k]);
            }
        }
        return this;
    };
    /**
     * 设置请求头部信息
     * @param header
     * @param value
     * @returns {ajax}
     */
    this.setHeader = function (header, value) {
        this.ajax.setRequestHeader(header, value);
        return this;
    };
    /**
     * 配置 multipart/form-data 格式
     * @returns {ajax}
     */
    this.form_data_config = function () {
        var delimiter = "FormBoundary" + this.randomString(24, 'nsS');
        this.form_data_delimiter = delimiter;
        this.form_data_delimiter_end = "\n--" + delimiter + "--";
        this.contentType = "multipart/form-data; boundary=" + delimiter;
        return this;
    };
    /**
     * multipart/form-data 格式数据生成，目前只支持文本上传
     * @param {string|object|array} field
     * @param value
     * @returns {string}
     */
    this.form_data = function (field, value) {
        var k, v, o = {}, list = [], k1, v1, fdd_end = this.form_data_delimiter_end;
        if (this.y_string(field) && this.y_string(value)) {
            var arr = ["--", this.form_data_delimiter, "\n", "Content-Disposition: form-data; name=\"", field, "\"", "\n\n", value];
            return arr.join("");
        } else if (this.is_object(field)) {
            for (k in field) {
                if (o.hasOwnProperty.call(field, k)) {
                    v = field[k];
                    this.y_string(v) && list.push(this.form_data(k, v));
                    this.is_array(v, 1) && this.y_string(v[0]) && this.y_string(v[1]) && list.push(this.form_data(v[0], v[1]));
                    this.is_object(v) && (k1 = this.vars(v.name, v.key, v.field)) && (v1 = this.vars(v.value, v.content)) && list.push(this.form_data(k1, v1));
                }
            }
            return list.join("\n") + fdd_end;
        } else if (this.is_array(field, 1)) {
            for (k in field) {
                if (o.hasOwnProperty.call(field, k)) {
                    v = field[k];
                    this.is_array(v, 1) && this.y_string(v[0]) && this.y_string(v[1]) && list.push(this.form_data(v[0], v[1]));
                    this.is_object(v) && (k1 = this.vars(v.name, v.key, v.field)) && (v1 = this.vars(v.value, v.content)) && list.push(this.form_data(k1, v1));
                }
            }
            return list.join("\n") + fdd_end;
        }
    };
    /**
     * 发送数据
     * @param url
     * @param data
     * @param success_fn
     * @param error_fn
     * @param obj_fn
     */
    this.send = function (url, data, success_fn, error_fn, obj_fn) {
        this.is_object(obj_fn) || (obj_fn = {});
        var self = this, xhr = this.ajax, even_list = ["loadstart", "load", "loadend", "progress", "error", "abort"], i, len;
        for (i = 0, len = even_list.length; i < len; i++) {
            (function (ev) {
                self.addEvent(xhr, ev, function (e) {
                    self.is_function(obj_fn[ev]) && obj_fn[ev].apply(xhr, arguments);
                });
            })(even_list[i]);
        }
        xhr.open(this.method, url, this.async);
        if (!this.is_formDate(data)) {
            this.y_string(this.contentType) && this.setHeader("Content-type", this.contentType);
            if (this.form_data_delimiter_end) {
                data = this.form_data(data);
            } else {
                (this.is_object(data) || this.is_array(data, 1)) && (data = this.data(data));
            }
        }
        this.addEvent(xhr, "readystatechange", function (e) {
            if (xhr.readyState === 4 && xhr.status === 200) {
                self.is_function(success_fn) && success_fn.call(xhr, xhr.responseText, xhr.response, xhr.status, xhr.readyState);
            } else {
                self.is_function(error_fn) && error_fn.call(xhr, xhr.responseText, xhr.response, xhr.status, xhr.readyState);
            }
        });
        xhr.send(data);
    };
    /**
     * POST请求
     * @param {string} url
     * @param {object|array} data 字段数据
     * @param {object|array} file_list 存放文件对象的对象或数组
     * @param config
     * @param success_fn
     * @param error_fn
     */
    this.post = function (url, data, file_list, config, success_fn, error_fn) {
        var k, v, o = {ok: 0}, arr = [], self = this;
        (this.is_array(data, 1) || this.is_object(data)) || (data = {});
        this.is_function(file_list) && (success_fn = file_list);
        this.is_function(config) && (error_fn = config);
        this.is_object(config) || (config = {});
        this.is_function(config.success) && (success_fn = config.success);
        this.is_function(config.error) && (error_fn = config.error);
        if (self.is_array(file_list, 1) || self.is_object(file_list)) {
            this.init("post", true).form_data_config();
            for (k in file_list) {
                v = file_list[k];
                if (o.hasOwnProperty.call(file_list, k) && (self.is_file(v) || self.is_blob(v))) {
                    k = /^\d+$/i.test(k) ? "file_" + k : k; //字段名称
                    arr.push({file: v, name: k});
                }
            }
            for (k in arr) {
                v = arr[k];
                if (o.hasOwnProperty.call(arr, k)) {
                    (function (v) {
                        self.file_reader(v.file, function (file, content) {
                            o.ok++;
                            data[v.name] = content;
                            if (o.ok >= arr.length) {
                                self.send(url, data, success_fn, error_fn, config.obj_fn);
                            }
                        });
                    })(v);
                }
            }
        } else {
            this.init("post", true);
            /(form-data|form|1)/i.test(config.type) && self.form_data_config();
            self.send(url, data, success_fn, error_fn, config.obj_fn);
        }
    };
    /**
     * GET请求
     * @param url
     * @param data
     * @param success_fn
     * @param error_fn
     * @param obj_fn
     */
    this.get = function (url, data, success_fn, error_fn, obj_fn) {
        this.init("GET", true, "default").send(url, data, success_fn, error_fn, obj_fn);
    };
}

ajax.prototype = {
    /**
     * 判断是否是数组
     * @param argument
     * @param {number|boolean} opt 是否用严格模式
     * @return {*}
     */
    is_array: function (argument, opt) {
        if (opt === 1 || opt === true) {
            return Object.prototype.toString.call(argument) === "[object Array]";
        }
        return argument && (typeof argument === "object") && ("length" in argument) && (typeof argument.length === "number");
    },
    /**
     * 判断是否是对象
     * @param argument
     * @return {*}
     */
    is_object: function (argument) {
        if (argument && (typeof argument === "object") && ("length" in argument)) {
            return Object.prototype.toString.call(argument) === "[object Object]";
        }
        return argument && (typeof argument === "object") && !("length" in argument);
    },
    /**
     * 判断是否是函数
     * @param argument
     * @return {*|boolean}
     */
    is_function: function (argument) {
        return typeof argument === "function";
    },
    /**
     * 判断是否是字符串
     * @param argument
     * @return {*|boolean}
     */
    is_string: function (argument) {
        return typeof argument === "string";
    },
    /**
     * 判断是否是数字
     * @param argument
     * @return {boolean}
     */
    is_number: function (argument) {
        return typeof argument === "number";
    },
    /**
     * 是否存在[true存在，false不存在]
     * @param argument
     * @param {array} opt 这里面的值是代表“不存在”
     * @return {boolean}
     */
    is_exist: function (argument, opt) {
        opt || (opt = ["", null, false, undefined]);
        for (var k in opt) {
            if (opt[k] === argument) {
                return false;
            }
        }
        return true;
    },
    /**
     * 是否是html标签元素
     * @param argument
     * @returns {boolean}
     */
    is_element: function (argument) {
        var reg = /^(\[object HTML[A-Za-z\-_\.]*Element\])$/i;
        return reg.test(Object.prototype.toString.call(argument));
    },
    /**
     * 是否是文件对象
     * @param argument
     * @returns {boolean}
     */
    is_file: function (argument) {
        return Object.prototype.toString.call(argument) === "[object File]";
    },
    is_blob: function (argument) {
        return this.type(argument) === "[object Blob]";
    },
    is_formDate: function (argument) {
        return this.type(argument) === "[object FormData]";
    },
    type: function (argument) {
        return Object.prototype.toString.call(argument);
    },
    /**
     * 是否是字符串（扩大范围）
     * @param {string|number} str
     * @returns {boolean|(*|boolean)}
     */
    y_string: function (str) {
        return this.is_exist(str) && (this.is_string(str) || this.is_number(str));
    },
    /**
     * 搜索数组中是否存在指定的值
     * @param {string} needle
     * @param {array} haystack
     * @param {boolean} argStrict
     * @return {boolean}
     */
    in_array: function (needle, haystack, argStrict) {
        var key = "", strict = !!argStrict;
        if (strict) {
            for (key in haystack) {
                if (haystack[key] === needle) {
                    return true
                }
            }
        } else {
            for (key in haystack) {
                if (haystack[key] == needle) {
                    return true
                }
            }
        }
        return false
    },
    /**
     * URL编码
     * @param str
     * @returns {string}
     */
    urlencode: function (str) {
        str = (str + "").toString();
        return encodeURIComponent(str).replace(/!/g, "%21").replace(/'/g, "%27").replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/\*/g, "%2A").replace(/%20/g, "+")
    },
    /**
     * URL解码
     * @param str
     * @returns {string}
     */
    urldecode: function (str) {
        return decodeURIComponent((str + "").replace(/%(?![\da-f]{2})/gi, function () {
            return "%25"
        }).replace(/\+/g, "%20"));
    },
    /**
     * 删除左右两端的空格
     * @param {string} str
     * @param {string} preg
     * @returns {void | string | *}
     */
    trim: function (str, preg) {
        if (preg) {
            preg = new RegExp("(^" + preg + "*|" + preg + "*$)", "ig");
        }
        (preg) || (preg = /(^[\s\t\r\n]*|[\s\t\r\n]*$)/ig);
        return str.replace(preg, "");
    },
    /**
     * 删除左侧的空格
     * @param {string} str
     * @param {string} preg
     * @returns {void | string | *}
     */
    ltrim: function (str, preg) {
        if (preg) {
            preg = new RegExp("(^" + preg + "*)", "ig");
        }
        (preg) || (preg = /(^[\s\t\r\n]*)/i);
        return str.replace(preg, "");
    },
    /**
     * 删除右侧的空格
     * @param {string} str
     * @param {string} preg
     * @returns {void | string | *}
     */
    rtrim: function (str, preg) {
        if (preg) {
            preg = new RegExp("(" + preg + "*$)", "ig");
        }
        (preg) || (preg = /([\s\t\r\n]*$)/i);
        return str.replace(preg, "");
    },
    vars: function () {
        for (var i = 0, len = arguments.length; i < len; i++) {
            if (this.is_exist(arguments[i])) {
                return arguments[i];
            }
        }
        return false;
    },
    /**
     * 返回异步请求对象
     * @returns {XMLHttpRequest}
     */
    xhr: function () {
        return window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject();
    },
    /**
     * 构建请求字符串
     * @param {string|Array|Object} field
     * @param {string} value
     * @param method
     * @returns {string}
     */
    data: function (field, value, method) {
        var k, v, arr = [], k1, v1;
        if (this.y_string(field) && this.y_string(value)) {
            return ([this.urlencode(field), this.urlencode(value)]).join("=");
        } else if (this.is_object(field)) {
            for (k in field) {
                if (({}).hasOwnProperty.call(field, k)) {
                    v = field[k];
                    this.y_string(v) && (arr.push(this.data(k, v, method)));
                    (this.is_array(v, 1) && this.y_string(v[0]) && this.y_string(v[1])) && arr.push(this.data(v[0], v[1], method));
                    (this.is_object(v) && (k1 = this.vars(v.name, v.key, v.field)) && (v1 = this.vars(v.value, v.content))) && arr.push(this.data(k1, v1, method));
                }
            }
            return arr.join("&");
        } else if (this.is_array(field, 1)) {
            //要求二维数据，如：[[],[]] 或者 [{},{}]
            for (k in field) {
                if (({}).hasOwnProperty.call(field, k)) {
                    v = field[k];
                    (this.is_array(v, 1) && this.y_string(v[0]) && this.y_string(v[1])) && arr.push(this.data(v[0], v[1], method));
                    (this.is_object(v) && (k1 = this.vars(v.name, v.key, v.field)) && (v1 = this.vars(v.value, v.content))) && arr.push(this.data(k1, v1, method));
                }
            }
            return arr.join("&");
        }
    },
    /* 事件监听 start */
    addEvent: function (obj, eventType, fn, boolean, event_fn) {
        (boolean) || (boolean = false);
        (event_fn) || (event_fn = "_event_fn");
        obj[eventType + event_fn] = fn;
        if (obj.addEventListener) {
            obj.addEventListener(eventType, obj[eventType + event_fn], boolean);
        } else if (obj.attachEvent) {
            obj.attachEvent("on" + eventType, obj[eventType + event_fn]);
        } else {
            obj["on" + eventType] = fn;
        }
    },
    removeEvent: function (obj, eventType, boolean, event_fn) {
        (boolean) || (boolean = false);
        (event_fn) || (event_fn = "_event_fn");
        if (obj.removeEventListener) {
            obj.removeEventListener(eventType, obj[eventType + event_fn], boolean);
        } else if (obj.detachEvent) {
            obj.detachEvent("on" + eventType, obj[eventType + event_fn]);
        } else {
            obj["on" + eventType] = null;
        }
    },
    getEvent: function (event) {
        return event ? event : window.event;
    },
    targetEvent: function (event) {
        return event ? event.target : event.srcElement;
    },
    preventDefault: function (event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    stopPropagation: function (event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    },
    /* 事件监听 end */
    /**
     * 生成随机字符串
     * @param {number} l 生成的字符串长度
     * @param {string} opt 选项，如字符串里面只含有数字那么值为 n
     * @param {object} obj
     * @return {string}
     */
    randomString: function (l, opt, obj) {
        var n = "0123456789", s = "abcdefghijklmnopqrstuvwxyz", S = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", str = "", C = {
            /*数字*/
            n: n,
            /*小写字母*/
            s: s,
            /*大写字母*/
            S: S,
            /*数字和小写字母*/
            ns: n + s,
            /*数字和大写字母*/
            nS: n + S,
            /*小写字母和大写字母*/
            sS: s + S,
            /*数字和小写字母和大写字母*/
            nsS: n + s + S,
            /*十六进制字符串*/
            hex: "0123456789abcdef",
            /*其他字符包括数字小写和大写*/
            all: n + s + S + "_+-*=^~,#$@|!"
        };
        (opt) || (opt = "n");
        if (this.is_object(obj)) {
            for (var attr in obj) {
                C[attr] = obj[attr];
            }
        }
        for (var i = 0, str_len = C[opt].length; i < l; i++) {
            str += C[opt][~~(Math.random() * str_len)];
        }
        return str;
    },
    file_reader: function (file, success_fn, error_fn) {
        var reader = new FileReader(), self = this;
        reader.readAsDataURL(file);
        reader.onload = function (e) {
            self.is_function(success_fn) && success_fn.call(reader, file, reader.result, e);
        };
        reader.onerror = function (e) {
            self.is_function(error_fn) && error_fn.call(reader, file, e);
        };
    }
};
(function () {
    var O = {writable: false, configurable: false, enumerable: false}; //设置对象属性不可修改，不可删除，不可枚举
    for (var attr in ajax.prototype) {
        Object.defineProperty(ajax.prototype, attr, O);
    }
})();
var xhr = new ajax();